﻿#pragma once

#include <learn/dll_ext.h>
#include <learn/math/math.h>
#include <learn/math/matrix.h>

#include <array>

namespace xi
{

namespace math
{

struct EXT_SYMBOL Matrix_Solver
{
  public:
    enum class Status
    {
        Uninitialized,
        Success,
        Failure,
        Singular,
    };

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_Solver(MatrixXd &&A);

    /**
     * @brief 获得状态
     *
     * @return Status
     */
    Status status() const;

    /**
     * @brief 获得状态
     *
     * @return Status&
     */
    Status &status();

    /**
     * @brief 移动矩阵
     *
     * @return MatrixXd&&
     */
    MatrixXd &&move_matrix();

    /**
     * @brief 计算接口
     *
     */
    virtual void perform();

    /**
     * @brief 求解接口
     *
     * @param[in] b
     * @return MatrixXd
     */
    virtual MatrixXd solve(MatrixXd &&b) = 0;

  protected:
    MatrixXd A;
    Status m_status;
};

class EXT_SYMBOL Matrix_L_Solver : public Matrix_Solver
{
  public:
    Matrix_L_Solver(Matrix_L_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_L_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_L_Solver(MatrixXd &&A);

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);
};

class EXT_SYMBOL Matrix_U_Solver : public Matrix_Solver
{
  public:
    Matrix_U_Solver(Matrix_U_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_U_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_U_Solver(MatrixXd &&A);

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);
};

class EXT_SYMBOL Matrix_LU_Solver : public Matrix_Solver
{
  public:
    Matrix_LU_Solver(Matrix_LU_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_LU_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_LU_Solver(MatrixXd &&A);

    /**
     * @brief LU 分解，返回状态
     *
     * @return void
     */
    void perform() override;

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);
};

class EXT_SYMBOL Matrix_PTLU_Solver : public Matrix_Solver
{
  public:
    Matrix_PTLU_Solver(Matrix_PTLU_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_PTLU_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_PTLU_Solver(MatrixXd &&A);

    /**
     * @brief LU 分解，返回状态
     *
     * @return void
     */
    void perform() override;

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);

  protected:
    std::vector<std::size_t> m_perm;
};

class EXT_SYMBOL Matrix_LLT_Solver : public Matrix_Solver
{
  public:
    Matrix_LLT_Solver(Matrix_LLT_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_LLT_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_LLT_Solver(MatrixXd &&A);

    /**
     * @brief LLT 分解，返回状态
     *
     * @return void
     */
    void perform() override;

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);
};

class EXT_SYMBOL Matrix_LDLT_Solver : public Matrix_Solver
{
  public:
    Matrix_LDLT_Solver(Matrix_LDLT_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_LDLT_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_LDLT_Solver(MatrixXd &&A);

    /**
     * @brief LLT 分解，返回状态
     *
     * @return void
     */
    void perform() override;

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);
};

class EXT_SYMBOL Matrix_Thomas_Solver : public Matrix_Solver
{
  public:
    Matrix_Thomas_Solver(Matrix_Thomas_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_Thomas_Solver(const Tridiagonal &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_Thomas_Solver(Tridiagonal &&A);

    /**
     * @brief 三对角分解，返回状态
     *
     * @return void
     */
    void perform() override;

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::tuple<Tridiagonal, MatrixXd, MatrixXd>
     */
    static std::tuple<Tridiagonal, MatrixXd, MatrixXd> example(std::size_t row, std::size_t col);
};

class EXT_SYMBOL Matrix_Blind_Solver : public Matrix_Solver
{
  public:
    Matrix_Blind_Solver(Matrix_Blind_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_Blind_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_Blind_Solver(MatrixXd &&A);

    /**
     * @brief 盲人爬山法分解，返回状态
     *
     * @return void
     */
    void perform() override;

    /**
     * @brief 计算矩阵范数 ||A^-1||_infty = ||b||_1
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, A^-1, x0
     *
     * @param[in] n
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t n);
};

class EXT_SYMBOL Matrix_Optimal_Solver : public Matrix_Solver
{
  public:
    Matrix_Optimal_Solver(Matrix_Optimal_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     * @param[in] x
     */
    Matrix_Optimal_Solver(const MatrixXd &A, const MatrixXd &x);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     * @param[in] x
     */
    Matrix_Optimal_Solver(MatrixXd &&A, MatrixXd &&x);

    /**
     * @brief 优化线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);

  protected:
    MatrixXd x;
};

class EXT_SYMBOL Matrix_Jacobi_Solver : public Matrix_Solver
{
  public:
    Matrix_Jacobi_Solver(Matrix_Jacobi_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_Jacobi_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_Jacobi_Solver(MatrixXd &&A);

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);

  public:
    double epsilon = Numeric::epsilon;
};

class EXT_SYMBOL Matrix_GaussSeidel_Solver : public Matrix_Solver
{
  public:
    Matrix_GaussSeidel_Solver(Matrix_GaussSeidel_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_GaussSeidel_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_GaussSeidel_Solver(MatrixXd &&A);

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);

  public:
    double epsilon = Numeric::epsilon;
};

class EXT_SYMBOL Matrix_SOR_Solver : public Matrix_Solver
{
  public:
    Matrix_SOR_Solver(Matrix_SOR_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_SOR_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_SOR_Solver(MatrixXd &&A);

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);

  public:
    double omega = 1.2;
    double epsilon = Numeric::epsilon;
};

class EXT_SYMBOL Matrix_QR_Solver : public Matrix_Solver
{
  public:
    Matrix_QR_Solver(Matrix_QR_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_QR_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_QR_Solver(MatrixXd &&A);

    /**
     * @brief QR 分解，返回状态
     *
     * @return void
     */
    void perform() override;

    /**
     * @brief 求解线性方程组的解，b 以行为输入，返回 x 以行为输出
     *
     * @param[in] b
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&b) override;

    /**
     * @brief 示例接口 A, x, b : A -> col x col * random, x, b -> row x col
     *
     * @param[in] row
     * @param[in] col
     * @return std::array<MatrixXd, 3>
     */
    static std::array<MatrixXd, 3> example(std::size_t row, std::size_t col);

  protected:
    VectorXd beta;
};

class EXT_SYMBOL Matrix_SymEigen_Solver : public Matrix_Solver
{
  public:
    Matrix_SymEigen_Solver(Matrix_SymEigen_Solver &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_SymEigen_Solver(const MatrixXd &A);

    /**
     * @brief 构造函数
     *
     * @param[in] A
     */
    Matrix_SymEigen_Solver(MatrixXd &&A);

    /**
     * @brief 对称阵特征分解
     *
     * @return void
     */
    void perform() override;

    /**
     * @brief 求解对称矩阵的特征值和特征向量，返回特征向量构成的正交阵
     *
     * @param[in] sigma 关值
     * @return MatrixXd
     */
    MatrixXd solve(MatrixXd &&sigma) override;

  protected:
    /**
     * @brief 沿着 p,q 分量旋转得到 JT*A*J, Q 累计 J
     *
     * @param[in] Q
     * @param[in] p
     * @param[in] q
     */
    MatrixXd rotate(MatrixXd &&Q, std::size_t p, std::size_t q);

  protected:
    double sigma;
};

} // namespace math

} // namespace xi